﻿using System;
using System.Windows.Forms;
using PCICH365;
using System.Threading;


/// <summary>
/// CDriverPowerCard 的摘要说明
/// </summary>
/// 

public enum PowerCardOutPutMode
{
    //0为恒流限压，1为恒压，2/3为关闭源输出
    CC,
    CV,
    Off
}
public enum PowerCardMeasMode
{
    //0为开启ADC 1ms平均模式，1为开启ADC实时测量模式    CC,
    Average = 0,
    Real,
    
}
public enum EnumRelayCode : ushort
{
    FW_Code = 0x4009,
    RV_Code = 0x4006,
};
public enum EnumReadCMD
{
    Vx,         // V = Vx/32768*30.0;   16bit，测量等待结束时的电压值，Vx= Vp(正极电压) – Vn(负极电压)
    Isense,     // I= Isense *10.0/32768/18.0/CurrR;    CurrR为当前电流档位下的感应电阻,16bit，测量等待结束时的电流值
    VxMax,      // V = VxMax /32768*30.0;   16bit，测量时间内Vx最大值，
    VxBegin,    //V = Vx_Begin /32768*30.0;    16bit，测量最初2ms内Vx的值，
    ADC1,       //V = ADCx/32768*30.0;    为电压感应ADC读数，16bit
    ADC2,
    ADC3,
    ADC4,
    ADC5,
    ADC6,
    ADC7,
    ADC8,       // I= ADC8 *10.0/32768/18.0/CurrR， CurrR为当前电流档位下的感应电阻 为电流感应ADC读数，16bit
    RangeInfo,  // 为当次测量结果的档位信息Range值。
}
public class CDriverPowerCard
{
    CH365Port PowerCard;
    public int open_flag = 0;
    //单位: A
    double[] NoiseCurrent = {0.01, 0.001, 0.000005, 0.0000001, 0.0000001};

    public int VALID_SMU =0;
    public enum EnumReg : int
    {
        //Adc复位寄存器，先写1再写0
        RW_ADC_RESET = 0xc0,

        /*****************************
        * 手动测试：1bit，默认为自动模式
        * 	        0： 自动测试——系统状态机运行模式"	
        * 	        1： 手动测试——可以校准，补偿等
        * 	        2/3：可编程模式
        * 
        *****************************/
        RW_Manual = 0X34,

        RW_ADC_INTEG = 0x48,//ADC采样间隔数， 单位50ns， 建议配置300

        /*****************************
        * 极限电压，决定了整个测试仪电源升压芯片输出的上限
        * 在使用过程中，由于芯片升压调节时间比较长，所以为了快速测试，
        * 请配置为一次测试中电压的最大值公式V/30.0*65536, 同测试仪
        *****************************/
        RW_VLIM = 0x12,

        //12bit， 设定命令RAM地址寄存器，先写地址寄存器，再写数据寄存器，FPGA自动完成RAM写操作
        W_SET_RAM_ADDR = 0x60,
        //16bit， 设定命令RAM数据寄存器，先写地址寄存器，再写数据寄存器，FPGA自动完成RAM写操作
        W_SET_RAM_DATA = 0x62,

        //12bit， 读取结果RAM地址寄存器，先写地址寄存器，再读数据寄存器
        RW_MEA_RAM_ADDR = 0x64,
        //16bit， 读取结果RAM数据寄存器，先写地址寄存器，再读数据寄存器
        R_MEA_RAM_DATA = 0x68,

        /*****************************
        *16bit， 编程测试起始和结束命令帧地址寄存器
        *[7:0]:   编程测试起始命令帧基址
        *[15:8]:  编程测试结束命令帧基址
        *****************************/
        RW_BEGIN_END_ADDR = 0x66,

        /*****************************
        *[0]  Insp_Begin 电测开始（ManualMode=0，2，3时有效），上升沿触发
        *[1]  SMU_OFF  电测结束后源表关闭输出（ManualMode=0，2，3时有效），高电平触发
        *[2]  Polar_Begin极性测试开始（仅ManualMode=0时有效），上升沿触发
        *[8]  Insp_End   电测结束标记（ManualMode=0，2，3时有效），高电平有效
        *****************************/
        RW_TEST_RESULT_STATE = 0x26,

        /* added by cjli 20191214 */
        RW_CALI_VOLT = 0x3A,
        /* end cjli */

        /* added by cjli 20200430 */
        W_RELAY_STABLE_TIME = 0xE0,
        W_LEVEL_CHANGE_TIME = 0xE4,
        /* end cjli */
    }

    public enum EnumWriteCMD
    {
        //: 恒压值，16bit，公式：CV_VAL  =  V*65535/30;
        CV_VAL,
        //: 恒流值，16bit，公式：CC_VAL  =  I*65535/(0.5/CurrR)
        CC_VAL,

        /*****************************
         * CCCV_MODE 占低8bit（0为恒流限压，1为恒压，2/3为关闭源输出）
         * Range占高8bit，如下表所示
         * 
         * CurrR为当前电流档位下的感应电阻，对应关系如下表所示：
         * Range值	档位范围	CurrR值
         * 0	(200mA, 1200mA]	0.2
         * 1	(10mA, 200mA]	2
         * 2	(100uA, 10mA]	39
         * 3	(10uA, 100uA]	3900
         * 4	(0,  10uA]	    39000
         * 
         * 电压档位   
         * Range值 档位范围     ATT衰减值
         * 0    (0, 20]         2
         * 1    (20, 50]        5
         * 2    (50, 100]       10
         * 3    (100, 200]      20
         **********************/
        MODE_Range,

        RalayConfig,        //16bit， 继电器输出，参考EXCEL的DEVICE文件，但是由于现在没有专门的IR测试电器，所以高2位请填1
        Waittime,           //16bit，测试等待时间，单位时0.1ms
        MeaMode,            //占高8位， 测量模式（0为开启ADC 1ms平均模式，1为开启ADC实时测量模式）；Mea_Base_Addr，占低8位，测量结果存放位置基地址
        CMD,
        PRECHARGE,
    };


    public struct CalibrationArray
    {
        public double expectation;
        public double setValue;
        public double iADC;
    };

    const int CAL_BUFF_SIZE = 96;
    static CalibrationArray[][][] VCalArray = new CalibrationArray[4][][];
    static CalibrationArray[][][] ICalArray = new CalibrationArray[4][][];


    public static ushort CardIndex = 0;
    double VLIM;//极限电压

    public CDriverPowerCard(ushort index)
    {
        PowerCard = new CH365Port();
        open_flag = 0;
        if (!PowerCard.Open(index))  //打开源表
        {
            MessageBox.Show("源表卡" + (index + 1) + "初始化失败。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
        }
        else
        {
            open_flag = 1;

          
            CardIndex = index;
    
        }

    }

    public bool WriteIoWord(ushort iIndex,int iAddr, ushort iWord)
    {
        return PowerCard.mWriteIoWord(iIndex,iAddr, iWord);
    }

    public bool ReadIoWord(ushort iIndex, int iAddr, ref ushort oDword)
    {
        return PowerCard.mReadIoWord(iIndex,iAddr, ref oDword);
    }


    public bool WriteADC_RESET(ushort iIndex)
    {
        bool result = true;
        result &= WriteIoWord(iIndex,(int)EnumReg.RW_ADC_RESET, 0x01);
        result &= WriteIoWord(iIndex,(int)EnumReg.RW_ADC_RESET, 0x00);
        return result;
    }

 



    public void VolLimitClear()
    {
        VLIM = 0;
    }

    public void VolLimitUpdate(double Vol)
    {
        if (VLIM < Vol)
        {
            VLIM = Vol;
        }
    }

    public bool WriteADC_VolLimit(ushort iIndex)
    {
        return WriteIoWord(iIndex,(int)EnumReg.RW_VLIM, VDriverToBin(VLIM, 0));
    }

    public bool WriteADC_INTEG(ushort iIndex,int us)
    {
        //return WriteIoWord((int)EnumReg.RW_ADC_INTEG, (ushort)((us + 25) / 50));
        return WriteIoWord(iIndex,(int)EnumReg.RW_ADC_INTEG, (ushort)(300));
    }

    /* added by cjli 20191214 */
    public bool WriteCaliVolt(ushort iIndex,int volt)
    {
        return WriteIoWord(iIndex,(int)EnumReg.RW_CALI_VOLT, (ushort)(volt * 65535 / 200.0));
    }
    /* end cjli */


    double GetDriverMinAccuracy_I(int iscal)
    {
        switch (iscal)
        {
            case 0: return 1.0 / (2.0 * 65535 * 0.2);
            case 1: return 1.0 / (2.0 * 65535 * 2);
            case 2: return 1.0 / (2.0 * 65535 * 39);
            case 3: return 1.0 / (2.0 * 65535 * 3900);
            default: return 1.0 / (2.0 * 65535 * 39000);
        }
    }

    /* modified by cjli 20191119 */
    double GetDriverMinAccuracy_V(int iscal)
    {
        double val = 0.0;
        switch (iscal)
        {
            case 0:
                val = 30.0 / 65535.0;
                break;
            case 1:
            case 2:
            case 3:
                val = 250.0 / 65535.0;
                break;
            default:
                val = 30.0 / 65535.0;
                break;
        }

        return val;
    }
    /* end cjli */

    public double RangeLimits(double val, double min, double max)
    {
        if (val < min)
            val = min;
        else if (val > max)
            val = max;
        return val;
    }


    public ushort IDriverToBin(double val, int iScal)
    {
        return (ushort)RangeLimits(val / GetDriverMinAccuracy_I(iScal), 0, 65535.0);
    }
    public ushort VDriverToBin(double val, int iScal)
    {
        /* modified by cjli 20191119 */
        if (iScal == 0)
        {
            return (ushort)RangeLimits(val / GetDriverMinAccuracy_V(iScal), 0, 65535.0);
        }
        else
        {
            return (ushort)RangeLimits((200 - val) / GetDriverMinAccuracy_V(iScal), 0, 65535.0);
        }
        /* end cjli */
    }

    public bool WriteRegCMD(ushort iIndex,uint iAddr, ushort iWord)
    {
        bool result = true;
        result &= WriteIoWord(iIndex,(int)EnumReg.W_SET_RAM_ADDR, (ushort)iAddr);
        result &= WriteIoWord(iIndex,(int)EnumReg.W_SET_RAM_DATA, (ushort)iWord);
        return result;
    }

#if true
    public bool WriteParamVolCurr(ushort iIndex, uint Id, PowerCardOutPutMode MODE, ushort V_VAL, int V_Scale, ushort I_VAL, int I_Scale, bool Bprecharge, int precharge_time)
    {
        bool result = true;
        uint addr = Id << 4;

        result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.CV_VAL, V_VAL);
        result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.CC_VAL, I_VAL);
        result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.MODE_Range, (ushort)((V_Scale << 12) + (I_Scale << 8) + MODE));
        if(Bprecharge && I_Scale>=3)
            result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.PRECHARGE, (ushort)((ushort)(0x8000)+(ushort)(precharge_time)));//(ushort)(0x83ff));
        else
            result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.PRECHARGE, (ushort)(0));//(ushort)(0x83ff));
        return result;
    }
#else
    public bool WriteParamVolCurr(ushort iIndex, uint Id, PowerCardOutPutMode MODE, double Driver_VAL, int Driver_Scale, double Meas_VAL, int Meas_Scale)
    {
        bool result = true;
        uint addr = Id << 4;

        if (MODE == PowerCardOutPutMode.CV)
        {
            /* added by cjli 20190107 for IR problem */
            //MODE = PowerCardOutPutMode.CC;
            /* end cjli */
            /* added by cjli 20180716 */
            Driver_VAL = CalcCalValue(ref VCalArray[iIndex][Driver_Scale], Driver_VAL);
            Meas_VAL = CalcCalValue(ref ICalArray[iIndex][Meas_Scale], Meas_VAL);
            /* end cjli */
            if (Driver_Scale == 0)
            {
                VolLimitUpdate(Driver_VAL);
            }
            result &= WriteRegCMD(iIndex,addr + (int)EnumWriteCMD.CV_VAL, VDriverToBin(Driver_VAL, Driver_Scale));
            result &= WriteRegCMD(iIndex,addr + (int)EnumWriteCMD.CC_VAL, IDriverToBin(Meas_VAL, Meas_Scale));
            result &= WriteRegCMD(iIndex,addr + (int)EnumWriteCMD.MODE_Range, (ushort)((Driver_Scale << 12) + (Meas_Scale << 8) + MODE));
        }
        else
        {
            /* added by cjli 20180716 */
            //int iScale = Driver_Scale;// GetCurrToScal(Driver_VAL);
            Driver_VAL = CalcCalValue(ref ICalArray[iIndex][Driver_Scale], Driver_VAL);
            Meas_VAL = CalcCalValue(ref VCalArray[iIndex][Meas_Scale], Meas_VAL);
            /* end cjli */
            if (Meas_Scale == 0)
            {
                VolLimitUpdate(Meas_VAL);
            }
            result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.CV_VAL, VDriverToBin(Meas_VAL, Meas_Scale));
            result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.CC_VAL, IDriverToBin(Driver_VAL, Driver_Scale));
            result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.MODE_Range, (ushort)((Meas_Scale << 12) + (Driver_Scale << 8) + MODE));
        }
        return result;
    }
#endif
    public bool WriteParamOther(ushort iIndex, uint cmdId, PowerCardMeasMode MeasMode, EnumRelayCode RalayConfig, double Waittime)
    {
        bool result = true;
        uint addr = cmdId << 4;
        result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.RalayConfig, (ushort)RalayConfig);
        result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.Waittime, (ushort)(10 * Waittime));
        result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.MeaMode, (ushort)(cmdId + ((ushort)MeasMode << 8)));
        result &= WriteRegCMD(iIndex, addr + (int)EnumWriteCMD.CMD, (ushort)(0));
        
        return result;
    }



    double GetMeasMinAccuracy_I(int iscal)
    {
        switch (iscal)
        {
            case 0: return (10.0 / 32768 / 18.0 / 0.2);
            case 1: return (10.0 / 32768 / 18.0 / 2);
            case 2: return (10.0 / 32768 / 18.0 / 39);
            case 3: return (10.0 / 32768 / 18.0 / 3900);
            default: return (10.0 / 32768 / 18.0 / 39000);
        }
    }


    double GetMeasMinAccuracy_V(int iscal)
    {     
        /* modified by cjli 20191210 */
#if false

#else
        double gain = 0.0;
        switch (iscal)
        {
            case 0:
                gain = 30.0 / 32768.0;//gain = 20.0 / 32768.0;
                break;
            case 1:
                gain = 210 / 32768.0;//gain = 220.0 / 32768.0;//60
                break;
            case 2:
                gain = 210 / 32768.0;//gain = 220.0 / 32768.0;//110
                break;
            case 3:
                gain = 210 / 32768.0;//gain = 220.0 / 32768.0;//210
                break;
            default:
                gain = 30.0 / 32768.0;//gain = 20.0 / 32768.0;//30
                break;
        }
        return gain;
#endif
        /* end cjli */
    }


    public bool ReadRegCMD(ushort iIndex, uint Id, EnumReadCMD Param, ref double val)
    {
        bool result = true;
        ushort iWord = 0;
        short readVal = 0;
        int scale = 0;
        ushort addr = (ushort)(Id << 4);

        for (int i = 0; i < 100; i++)
        {
            ;
        }

        result &= WriteIoWord(iIndex, (int)EnumReg.RW_MEA_RAM_ADDR, (ushort)(addr + Param));
        result &= WriteIoWord(iIndex, (int)EnumReg.RW_MEA_RAM_ADDR, (ushort)(addr + Param));
        
        for (int i = 0; i < 100; i++)
        {
            ;
        }

        result &= ReadIoWord(iIndex, (int)EnumReg.R_MEA_RAM_DATA, ref iWord);
        result &= ReadIoWord(iIndex, (int)EnumReg.R_MEA_RAM_DATA, ref iWord);

        readVal = (short)iWord;
      
        if (readVal < 0)
        {
            readVal = 0;
        }
        val = (double)readVal;

        WriteIoWord(iIndex, (int)EnumReg.RW_MEA_RAM_ADDR, (ushort)(addr + 15));
        ReadIoWord(iIndex, (int)EnumReg.R_MEA_RAM_DATA, ref iWord);
        scale = (int)iWord;
      
        switch (Param)
        {
            case EnumReadCMD.Vx:    // V = Vx/32768*30.0;   16bit，测量等待结束时的电压值，Vx= Vp(正极电压) – Vn(负极电压)
                scale = scale / 16;
                val *= GetMeasMinAccuracy_V(scale);
                
                break;
            case EnumReadCMD.Isense: // I= Isense *10.0/32768/18.0/CurrR;    CurrR为当前电流档位下的感应电阻,16bit，测量等待结束时的电流值
                scale = scale % 16;
                val *= GetMeasMinAccuracy_I(scale);
                
                break;
            case EnumReadCMD.VxMax:     // V = VxMax /32768*30.0;   16bit，测量时间内Vx最大值，
                scale = scale / 16;
                val *= GetMeasMinAccuracy_V(scale);
               
                break;
            case EnumReadCMD.VxBegin:   //V = Vx_Begin /32768*30.0;    16bit，测量最初2ms内Vx的值，
                scale = scale / 16;
                val *= GetMeasMinAccuracy_V(scale);
                val = CalcCalValueIADC(ref VCalArray[iIndex][scale], val);
               
                break;

            /* added by cjli 20191210 */
            //case EnumReadCMD.ADC1:      //V = ADCx/32768*30.0;    为电压感应ADC读数，16bit
            case EnumReadCMD.ADC2:
            case EnumReadCMD.ADC3:
            case EnumReadCMD.ADC4:
            case EnumReadCMD.ADC5:
            case EnumReadCMD.ADC6:
            case EnumReadCMD.ADC7:
            case EnumReadCMD.ADC8:
                scale = scale / 16;
                val *= GetMeasMinAccuracy_V(4);
               
                break;
            // I= ADC8 *10.0/32768/18.0/CurrR， CurrR为当前电流档位下的感应电阻 为电流感应ADC读数，16bit
            case EnumReadCMD.ADC1:
                scale = scale % 16;
                val *= GetMeasMinAccuracy_I(scale);
                break;
            default: break;
        }

        return result;
    }


    public bool WriteRegTestCmdRange(ushort iIndex, uint IdStart, uint IdEnd)
    {
        bool result = true;
        result &= WriteIoWord(iIndex, (int)EnumReg.RW_BEGIN_END_ADDR, (ushort)(IdStart + (IdEnd << 8)));
        return result;
    }

    public bool WriteRegMeasBegin(ushort iIndex,bool bBegin, bool bAutoOffPower = true, bool bEnPolarity = false)
    {
        bool result = true;
        int val = 0;
        val |= (!bBegin) ? 0 : (1 << 0);
        val |= (!bAutoOffPower) ? 0 : (1 << 1);
        val |= (!bEnPolarity) ? 0 : (1 << 2);

        result &= WriteIoWord(iIndex, (int)EnumReg.RW_TEST_RESULT_STATE, 0);
        result &= WriteIoWord(iIndex, (int)EnumReg.RW_TEST_RESULT_STATE, (ushort)val);
        return result;
    }

    public bool WriteRegMeasEnd(ushort iIndex,bool bBegin, bool bAutoOffPower = true, bool bEnPolarity = false)
    {
        bool result = true;
        int val = 0;
        val |= (!bBegin) ? 0 : (1 << 0);
        val |= (!bAutoOffPower) ? 0 : (1 << 1);
        val |= (!bEnPolarity) ? 0 : (1 << 2);

        result &= WriteIoWord(iIndex, (int)EnumReg.RW_TEST_RESULT_STATE, (ushort)val);
        return result;
    }

    public bool ReadRegMeasEnd(ushort iIndex,ref bool IsEnd)
    {
        bool result = true;
        ushort val = 0;
        result &= ReadIoWord(iIndex,(int)EnumReg.RW_TEST_RESULT_STATE, ref val);
        IsEnd = result && ((val & 0x100) == 0x100);
        return result;
    }



    int GetCurrToScal(double val)
    {
        int current_ctrl;
        if (val <= 0.00001)
            current_ctrl = 4;
        else if (val <= 0.0001)
            current_ctrl = 3;
        else if (val <= 0.01)
            current_ctrl = 2;
        else if (val <= 0.2)
            current_ctrl = 1;
        else current_ctrl = 0;
        return current_ctrl;
    }


    double Lambda(double p1, double p2, double p)
    {
        if (p2 != p)
        {
            return (p - p1) / (p2 - p);
        }
        else
        {
            return 1e10;
        }
    }

    double LambdaPoint(double p1, double p2, double lanbda)
    {
        return (lanbda * p2 + p1) / (1 + lanbda);
    }

    double CalcCalValue(ref CalibrationArray[] arry, double val)
    {
        double calvalue = val;
        CalibrationArray cal1, cal2;

        for (int i = 0; arry[i].expectation > 0; i++)
        {
            if (arry[i].expectation >= val)
            {
                cal2 = arry[i];
                if (i == 0)
                {
                    cal1.expectation = 0.0;
                    cal1.setValue = 0.0;
                    cal1.iADC = 0.0;
                }
                else
                {
                    cal1 = arry[i - 1];
                }

                if (cal2.expectation == val)
                {
                    calvalue = cal2.setValue;
                }
                else
                {
                    double lanbda = Lambda(cal1.expectation, cal2.expectation, val);
                    calvalue = LambdaPoint(cal1.setValue, cal2.setValue, lanbda);
                }
                break;
            }
        }

        return calvalue;
    }


    public double CalcCalValueIADC(ref CalibrationArray[] arry, double val)
    {
        CalibrationArray cal1, cal2;
        double calvalue = val;

        for (int i = 0; arry[i].iADC > 0; i++)
        {
            if (arry[i].iADC >= val)
            {
                cal2 = arry[i];
                if (i == 0)
                {
                    cal1.expectation = 0.0;
                    cal1.setValue = 0.0;
                    cal1.iADC = 0.0;
                }
                else
                {
                    cal1 = arry[i - 1];
                }

                if (cal2.iADC == val)
                {
                    calvalue = cal2.expectation;
                }
                else
                {
                    calvalue = LambdaPoint(cal1.expectation,
                                            cal2.expectation,
                                            Lambda(cal1.iADC, cal2.iADC, val));
                }

                break;
            }
        }

        return calvalue;
    }
    
    public void SMU_ON(ushort iIndex, ushort I_range, ushort I_val, ushort V_range, ushort V_val, ushort CCCV_MODE, ushort Relay_Flag)
    {
        WriteIoWord(iIndex, 0x36, 0); //关闭继电器输出
        Thread.Sleep(10);
        WriteIoWord(iIndex, 0xC0, (ushort)(I_range | (V_range << 4))); //电压电流档位设定
        WriteIoWord(iIndex, 0x42, V_val);
        WriteIoWord(iIndex, 0x40, I_val);
        WriteIoWord(iIndex, 0x44, 3);
        Thread.Sleep(10);
        WriteIoWord(iIndex, 0x36, Relay_Flag);
        Thread.Sleep(20);

        WriteIoWord(iIndex, 0x42, V_val);
        WriteIoWord(iIndex, 0x40, I_val);
        WriteIoWord(iIndex, 0x44, CCCV_MODE);
       
  
    }

    public void SMU_ON_VFD(ushort iIndex, ushort I_range, ushort I_val, ushort V_range, ushort V_val, ushort CCCV_MODE, ushort Relay_Flag)
    {
        WriteIoWord(iIndex, 0x36, 0); //关闭继电器输出
        Thread.Sleep(10);
        WriteIoWord(iIndex, 0xC0, (ushort)(I_range | (V_range << 4))); //电压电流档位设定
        WriteIoWord(iIndex, 0x42, V_val);
        WriteIoWord(iIndex, 0x40, I_val);
        WriteIoWord(iIndex, 0x44, 3);
        Thread.Sleep(10);
        WriteIoWord(iIndex, 0x36, Relay_Flag);
        Thread.Sleep(20);

        WriteIoWord(iIndex, 0x42, V_val);
        WriteIoWord(iIndex, 0x40, I_val);
        WriteIoWord(iIndex, 0x44, CCCV_MODE);
       

    }




    public void SMU_0FF(ushort iIndex)
    {
        WriteIoWord(iIndex, 0x36, 0);
        Thread.Sleep(10);
        WriteIoWord(iIndex, 0x42, 0);
        WriteIoWord(iIndex, 0x40, 0);
        WriteIoWord(iIndex, 0x44, 3);
        Thread.Sleep(10);
    }

    public double SMU_ReadV(ushort iIndex, ushort V_Range)
    {
        double val = 0.0;
        ushort Vp = 0;
        ushort Vn = 0;
        ReadIoWord(iIndex, 0x0058, ref Vp);
        ReadIoWord(iIndex, 0x004C, ref Vn);

        val = ((short)Vp-(short)Vn)*GetMeasMinAccuracy_V(V_Range);
        //val = ((short)Vp) * GetMeasMinAccuracy_V(V_Range);
        return Math.Abs(val);
    }

    public double SMU_ReadI(ushort iIndex, ushort I_Range)
    {
        double val = 0.0;
        ushort Isense = 0;
        
        ReadIoWord(iIndex, 0x004A, ref Isense);


        val = ((short)Isense) * GetMeasMinAccuracy_I(I_Range);
        if (val < 0) val = 0;
        return val;
    }
    public double Cali_Vset(int Vscale, double V_set)
    {
        return CalcCalValue(ref VCalArray[CardIndex][Vscale], V_set);

    }

    public double Cali_Iset(int Iscale, double Iset)
    {
        return CalcCalValue(ref ICalArray[CardIndex][Iscale], Iset);

    }

    public double Cali_ResultV(int Vscale, double V_val)
    {
        return CalcCalValueIADC(ref VCalArray[CardIndex][Vscale], V_val);
    }
    public double Cali_ResultI(int Iscale, double I_val)
    {
        return CalcCalValueIADC(ref ICalArray[CardIndex][Iscale], I_val);
    }
    /*
     * iIndex: 板卡号  0
     * data[]  数据，无符号16bit
     * size  16bit数据的个数 一次写入 size = 32*3*9
     * offset 偏移， 一次写入 offset =0
     */
    public unsafe bool WriteFlash(ushort iIndex,  ushort[] data, int size, ushort offset)
    {
        ushort i = 0;
        ushort WR_FLASH_DONE = 0;
        ushort DATAL = 0;
        ushort DATAH = 0;

        

        for (i = 0; i < size; i++)
        {
            DATAL = (ushort)(data[i] & 0xFF);
            DATAH = (ushort)((data[i] >> 8) & 0xFF);
            WriteIoWord(iIndex, 0x90, DATAL);                  //写数据  低8bit
            WriteIoWord(iIndex, 0x92, (ushort)(offset + 2 * i));				        //写地址  12bit  
            WriteIoWord(iIndex, 0x90, DATAH);         //写数据  高8bit
            WriteIoWord(iIndex, 0x92, (ushort)(offset + 2 * i + 1));				           //写地址  12bit 
           
        }
        Thread.Sleep(10);
        WriteIoWord(iIndex, 0x94, 0);   				//启动读， 先0后1
        WriteIoWord(iIndex, 0x94, 1);
        int cnt = 25;
        while (cnt>=0)
        {
            Thread.Sleep(200);
            if (!ReadIoWord(iIndex, 0x94, ref WR_FLASH_DONE))				//等待FLASH写完成
                break;
            if (((uint)WR_FLASH_DONE & 0x0001) == 0x0001) break;
            cnt--;
        }
        if (cnt < 0) return false;

        return true;
    }


    public unsafe bool ReadFlash(ushort iIndex, ref ushort[] data, int size, ushort offset)
    {
        ushort i = 0;
        ushort RD_FLASH_DONE = 0;
        ushort DATAL = 0;
        ushort DATAH = 0;

        WriteIoWord(iIndex,0x96, 0);   				//启动读， 先0后1
        WriteIoWord(iIndex,0x96, 1);
        int cnt = 25;
        while (cnt >= 0)
        {
            Thread.Sleep(200);
            if (!ReadIoWord(iIndex, 0x96, ref RD_FLASH_DONE))				//等待FLASH读完成
                break;
            if (((uint)RD_FLASH_DONE & 0x0001) == 0x0001) break;
            cnt--;
        }
       
        for (i = 0; i < size; i++)
        {
            WriteIoWord(iIndex, 0x98, (ushort)(offset + 2 * i));     		//写地址  12bit 
            ReadIoWord(iIndex, 0x9A, ref DATAL);				            //读数据  低8bit
            WriteIoWord(iIndex, 0x98, (ushort)(offset + 2 * i + 1));        //写地址  12bit  
            ReadIoWord(iIndex, 0x9A, ref DATAH);				            //读数据  高8bit
            data[i] = (ushort)((DATAL & 0xFF) + (DATAH & 0xFF) * 0x100);
        }
        if (cnt < 0) return false;
        return true;
    }


    double GetMinResolution_I(int iscal)
    {
        switch (iscal)
        {
            case 0: return 0.5 / 0.2 / 65535;//0.5 / 65535
            case 1: return 0.5 / 2 / 65535;
            case 2: return 0.5 / 39 / 65535;
            case 3: return 0.5 / 3900 / 65535;
            default: return 0.5 / 39000 / 65535;
        }
    }
    double GetADCResolution_I(int iscal)
    {
        switch (iscal)
        {
            case 0: return 10.0 / 18.0 / 32768 / 0.2;
            case 1: return 10.0 / 18.0 / 32768 / 2;
            case 2: return 10.0 / 18.0 / 32768 / 39;
            case 3: return 10.0 / 18.0 / 32768 / 3900;
            default: return 10.0 / 18.0 / 32768 / 39000;
        }
    }



    public void LoadPowerCardCal(ushort iIndex)
    {

        ushort[] calbuff = new ushort[CAL_BUFF_SIZE];
       
        
        /*
        VCalArray[0].expectation = 0;
        VCalArray[0].setValue = 0;
        VCalArray[1].expectation = 50;
        VCalArray[1].setValue = 50;
        */
        try
        {
          
            VCalArray[iIndex] = new CalibrationArray[4][];
            ICalArray[iIndex] = new CalibrationArray[5][];
            for (int iscal = 0; iscal < 4; iscal++)
            {
                VCalArray[iIndex][iscal] = new CalibrationArray[CAL_BUFF_SIZE];
                Array.Clear(calbuff, 0, calbuff.Length);
                ReadFlash(iIndex, ref calbuff, calbuff.Length, (ushort)((iscal * CAL_BUFF_SIZE + 0) * sizeof(ushort)));
                for (int i = 0; calbuff[3 * i + 0] > 0; i++)
                {
                    VCalArray[iIndex][iscal][i].expectation = (iscal == 0) ? (calbuff[3 * i + 0] * GetDriverMinAccuracy_V(iscal)) : (200 - calbuff[3 * i + 0] * GetDriverMinAccuracy_V(iscal));
                    VCalArray[iIndex][iscal][i].setValue = (iscal == 0) ? (calbuff[3 * i + 1] * GetDriverMinAccuracy_V(iscal)) : (200 - calbuff[3 * i + 1] * GetDriverMinAccuracy_V(iscal));
                    VCalArray[iIndex][iscal][i].iADC = calbuff[3 * i + 2] * GetMeasMinAccuracy_V(iscal);
                }
            }



            for (int iscal = 0; iscal < 5; iscal++)
            {
                ICalArray[iIndex][4 - iscal] = new CalibrationArray[CAL_BUFF_SIZE];

                Array.Clear(calbuff, 0, calbuff.Length);
                ReadFlash(iIndex,ref calbuff, calbuff.Length, (ushort)((iscal * CAL_BUFF_SIZE + 96 * 4) * sizeof(ushort)));
                for (int i = 0; calbuff[3 * i + 0] > 0; i++)
                {
                    ICalArray[iIndex][4 - iscal][i].expectation = calbuff[3 * i + 0] * GetMinResolution_I(4 - iscal);
                    ICalArray[iIndex][4 - iscal][i].setValue = calbuff[3 * i + 1] * GetMinResolution_I(4 - iscal);
                    ICalArray[iIndex][4 - iscal][i].iADC = calbuff[3 * i + 2] * GetADCResolution_I(4 - iscal);
                }
            }
          
        }
        catch (Exception e)
        {
            MessageBox.Show("源表" + iIndex + "没有校正数据！", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification);
        }
    }

    /* added by cjli 20200430 */
    void SetStableTime(ushort iIndex, ushort iRelayTime, ushort iLevelChangeTime)
    {
        WriteIoWord(iIndex, (int)EnumReg.W_RELAY_STABLE_TIME, iRelayTime);
        WriteIoWord(iIndex, (int)EnumReg.W_LEVEL_CHANGE_TIME, iLevelChangeTime);
    }
    /* end cjli */

    //读取IO寄存器中bit0-15的方法
    private ushort registerValue = 0;
    public bool ReadDIOBit(ushort iIndex, int bitPosition)
    {
        ReadIoWord(iIndex, 0x24, ref registerValue);
        bool bitValue = ((registerValue >> bitPosition) & 0x01) == 0x01;// 使用位运算来获取指定位(bitPosition位)的值
        return bitValue;
    }
    //改写bit0-9,13-15的方法  
    public void WriteDIOBit(ushort iIndex, int bitPosition,bool newValue)
    {
        if (newValue==true) { registerValue |= (ushort)(1 << bitPosition);}// 将bitPosition位设置为1
        else if(newValue==false) { registerValue &= (ushort)~(1 << bitPosition); }// 将bitPosition位设置为0
        WriteIoWord(iIndex, 0x24, registerValue);// 使用WriteIoWord指令写入更新后的寄存器值0
    }
}

